<script setup lang="ts">
import { ref, watch } from "vue";
import * as XLSX from "xlsx";
import "./Index.css";

function showMessage(type: "success" | "error", text: string) {
  const globalMsg = (window as any)?.ElMessage;
  if (globalMsg && typeof globalMsg[type] === "function") {
    globalMsg[type](text);
  } else {
    if (type === "error") {
      console.error(text);
    } else {
      console.log(text);
    }
  }
}

const props = defineProps<{
  disabled?: boolean;
  skuList?: any[];
  staffList?: any[];
}>();
const emit = defineEmits<{
  (
    e: "uploaded",
    file: Array<{
      flowerName?: string;
      workNumber?: string;
      title?: string;
      primaryRegion?: string;
      secondaryRegion?: string;
      store?: string;
      position?: string;
      track?: string;
      examTotalScore?: number;
      practiceTotalScore?: number;
      examCompletedQuestions?: number;
      examIncorrectQuestions?: number;
      examTotalQuestions?: number;
      practiceCompletedQuestions?: number;
      practiceIncorrectQuestions?: number;
      practiceTotalQuestions?: number;
    }> | null,
  ): void;
}>();

const fileInput = ref<HTMLInputElement | null>(null);

const parsedData = ref<any[]>([]);
const fileName = ref<string>("");
const lastUploadedFileName = ref<string>("");
const successNotice = ref<string>("");
const allGoodsNumber = ref<number>(0);
const uploadErrorText = ref<string>("");
const uploadState = ref<"init" | "success" | "error">("init");

function triggerFilePicker() {
  if (fileInput.value) fileInput.value.click();
}

const isDragging = ref<boolean>(false);

function onDragOver(e: DragEvent) {
  e.preventDefault();
  isDragging.value = true;
}

function onDragLeave(e: DragEvent) {
  e.preventDefault();
  isDragging.value = false;
}

function onDrop(e: DragEvent) {
  e.preventDefault();
  isDragging.value = false;
  const files = e.dataTransfer?.files;
  if (files && files.length > 0) {
    handleFile(files[0]);
  }
}

watch(
  () => [props.staffList, props.skuList],
  ([staffList, skuList]) => {
    const list =
      staffList && staffList.length > 0
        ? staffList
        : skuList && skuList.length > 0
          ? skuList
          : null;
    if (list && list.length > 0) {
      allGoodsNumber.value = list.length;
      fileName.value = "已导入文件";
      uploadState.value = "success";
    }
  },
);

function handleFile(file: File) {
  const reader = new FileReader();
  reader.onload = (event) => {
    try {
      const arrayBuffer = event.target?.result as ArrayBuffer;
      const workbook = XLSX.read(arrayBuffer, { type: "array" });
      const sheetName = workbook.SheetNames[0];
      const sheet = workbook.Sheets[sheetName];

      // Read sheet as rows to detect header positions reliably
      const rows = XLSX.utils.sheet_to_json(sheet, {
        header: 1,
        defval: "",
      }) as any[];
      if (!rows || rows.length < 2) {
        uploadState.value = "error";
        uploadErrorText.value = "文件为空或无有效数据";
        showMessage("error", uploadErrorText.value);
        return;
      }

      // Attempt to find the actual header row among the first few rows (handles title rows / merged cells)
      const maxScan = Math.min(5, rows.length);
      const rawHeaderRows: string[][] = [];
      for (let r = 0; r < maxScan; r++) {
        rawHeaderRows.push(
          (rows[r] || []).map((h: any) => (h || "").toString().trim()),
        );
      }

      const patternsMap: { [key: string]: RegExp[] } = {
        workNumber: [/^工号$/i, /^id$/i, /work.?number/i, /工号/i],
        flowerName: [/^姓名$/i, /花名/i, /name/i],
        track: [/赛道/i, /track/i],
        title: [/上届称号/i, /称号/i, /title/i],
        primaryRegion: [/一级组织/i, /primary/i, /primary.?region/i],
        secondaryRegion: [/二级组织/i, /secondary/i, /secondary.?region/i],
        store: [/部门/i, /门店/i, /store/i],
        position: [/职位/i, /岗位/i, /position/i],
        practiceTotalQuestions: [
          /练习总题目数/i,
          /practice.*total/i,
          /practiceTotalQuestions/i,
        ],
        practiceCompletedQuestions: [
          /练习已完成/i,
          /practice.*completed/i,
          /practiceCompletedQuestions/i,
        ],
        practiceTotalScore: [
          /练习累计得分/i,
          /practice.*score/i,
          /practiceTotalScore/i,
        ],
        practiceIncorrectQuestions: [
          /练习错题数/i,
          /练习.*错题/i,
          /practice.*wrong/i,
        ],
        examTotalQuestions: [
          /考试总题目数/i,
          /exam.*total/i,
          /examTotalQuestions/i,
        ],
        examCompletedQuestions: [
          /考试已完成/i,
          /exam.*completed/i,
          /examCompletedQuestions/i,
        ],
        examTotalScore: [/考试累计得分/i, /exam.*score/i, /examTotalScore/i],
        examIncorrectQuestions: [/考试错题数/i, /考试.*错题/i, /exam.*wrong/i],
      };

      const normalizeHeaderText = (h: any) => {
        const s = (h || "").toString();
        // remove right-hand suffixes like '=id' or ':id'
        const left = s.split(/=|＝|:|：/)[0];
        return left
          .toString()
          .toLowerCase()
          .replace(/[\s\-–—()（）\[\]"'`·,。.]/g, "")
          .trim();
      };

      let bestIndex = 0;
      let bestScore = -1;
      let headerRow: string[] = rawHeaderRows[0] || [];
      let normalizedHeaderRow: string[] = headerRow.map((h) =>
        normalizeHeaderText(h),
      );

      const scoreHeader = (candidate: string[]) => {
        const norm = candidate.map((h) => normalizeHeaderText(h));
        let score = 0;
        Object.values(patternsMap).forEach((patterns) => {
          const found = candidate.find((h, idx) =>
            patterns.some((p) => p.test(h) || p.test(norm[idx])),
          );
          if (found) score++;
        });
        return { score, norm };
      };

      for (let r = 0; r < rawHeaderRows.length; r++) {
        const candidate = rawHeaderRows[r];
        const { score, norm } = scoreHeader(candidate);
        if (score > bestScore) {
          bestScore = score;
          bestIndex = r;
          headerRow = candidate;
          normalizedHeaderRow = norm;
        }
      }

      const findIndex = (patterns: RegExp[]) =>
        headerRow.findIndex((h, idx) =>
          patterns.some((p) => p.test(h) || p.test(normalizedHeaderRow[idx])),
        );

      const idxMap: { [key: string]: number } = {};
      Object.keys(patternsMap).forEach((key) => {
        idxMap[key] = findIndex(patternsMap[key]);
      });

      // Require workNumber (工号) at least
      if (idxMap.workNumber === -1) {
        uploadState.value = "error";
        uploadErrorText.value = "未能识别 工号(id) 列，请检查表头";
        showMessage("error", uploadErrorText.value);
        return;
      }

      const toNumber = (v: any) => {
        const s = (v ?? "").toString().replace(/,/g, "").trim();
        if (s === "") return undefined;
        const n = Number(s);
        return isNaN(n) ? undefined : n;
      };

      const newList: any[] = [];
      const dataStartIndex = bestIndex + 1;
      for (let i = dataStartIndex; i < rows.length; i++) {
        const row = rows[i] || [];
        const workNumber = (row[idxMap.workNumber] ?? "").toString().trim();
        if (!workNumber) continue;

        const record: any = {};
        record.workNumber = workNumber;
        if (idxMap.flowerName > -1)
          record.flowerName = (row[idxMap.flowerName] ?? "").toString().trim();
        if (idxMap.track > -1)
          record.track = (row[idxMap.track] ?? "").toString().trim();
        if (idxMap.title > -1)
          record.title = (row[idxMap.title] ?? "").toString().trim();
        if (idxMap.primaryRegion > -1)
          record.primaryRegion = (row[idxMap.primaryRegion] ?? "")
            .toString()
            .trim();
        if (idxMap.secondaryRegion > -1)
          record.secondaryRegion = (row[idxMap.secondaryRegion] ?? "")
            .toString()
            .trim();
        if (idxMap.store > -1)
          record.store = (row[idxMap.store] ?? "").toString().trim();
        if (idxMap.position > -1)
          record.position = (row[idxMap.position] ?? "").toString().trim();

        if (idxMap.practiceTotalQuestions > -1)
          record.practiceTotalQuestions = toNumber(
            row[idxMap.practiceTotalQuestions],
          );
        if (idxMap.practiceCompletedQuestions > -1)
          record.practiceCompletedQuestions = toNumber(
            row[idxMap.practiceCompletedQuestions],
          );
        if (idxMap.practiceTotalScore > -1)
          record.practiceTotalScore = toNumber(row[idxMap.practiceTotalScore]);
        if (idxMap.practiceIncorrectQuestions > -1)
          record.practiceIncorrectQuestions = toNumber(
            row[idxMap.practiceIncorrectQuestions],
          );
        if (idxMap.examTotalQuestions > -1)
          record.examTotalQuestions = toNumber(row[idxMap.examTotalQuestions]);
        if (idxMap.examCompletedQuestions > -1)
          record.examCompletedQuestions = toNumber(
            row[idxMap.examCompletedQuestions],
          );
        if (idxMap.examTotalScore > -1)
          record.examTotalScore = toNumber(row[idxMap.examTotalScore]);
        if (idxMap.examIncorrectQuestions > -1)
          record.examIncorrectQuestions = toNumber(
            row[idxMap.examIncorrectQuestions],
          );

        if (!newList.some((n) => n.workNumber === workNumber)) {
          newList.push(record);
        }
      }

      if (newList.length === 0) {
        uploadState.value = "error";
        uploadErrorText.value = "未解析出任何有效记录";
        showMessage("error", uploadErrorText.value);
        return;
      }

      if (newList.length > 10000) {
        uploadState.value = "error";
        uploadErrorText.value = "导入条目不能超过10000条";
        showMessage("error", uploadErrorText.value);
        return;
      }

      parsedData.value = newList;
      emit("uploaded", newList);
      uploadState.value = "success";
      allGoodsNumber.value = newList.length;
      fileName.value = file.name;
      lastUploadedFileName.value = file.name;
      showMessage("success", "文件解析成功");
    } catch (err) {
      uploadState.value = "error";
      uploadErrorText.value = "识别失败，导入内容有误";
      showMessage("error", uploadErrorText.value);
    }
  };
  reader.readAsArrayBuffer(file);
}

function onFileChange(e: Event) {
  const files = (e.target as HTMLInputElement).files;
  if (files && files.length > 0) {
    handleFile(files[0]);
  } else {
    emit("uploaded", null);
  }
}

function handleResetData() {
  // clear internal parsed data
  parsedData.value = [];
  // reset counters and UI state to initial
  allGoodsNumber.value = 0;
  uploadState.value = "init";
  fileName.value = "";
  uploadErrorText.value = "";
  // reset drag state
  isDragging.value = false;
  // clear native file input so the same file can be re-selected
  if (fileInput.value) {
    try {
      fileInput.value.value = "";
    } catch (e) {
      // ignore
    }
  }
  // emit null to indicate no file (initial state)
  emit("uploaded", null);
}

// New: saving state and handler to POST parsed data to publishAnswerInfo
const isSaving = ref(false);

async function handleSave() {
  if (isSaving.value) return;
  if (!parsedData.value || parsedData.value.length === 0) {
    showMessage("error", "没有可保存的数据");
    return;
  }

  // assemble payload: each item must have workNumber and answerInfo (JSON string)
  // We'll compute score as sum of examTotalScore and practiceTotalScore when available
  const items = parsedData.value.map((rec: any) => {
    const workNumber = Number(rec.workNumber);
    // compute score as sum of examTotalScore and practiceTotalScore (treat missing as 0)
    const examScore = Number(rec.examTotalScore ?? 0) || 0;
    const practiceScore = Number(rec.practiceTotalScore ?? 0) || 0;
    const totalScore = examScore + practiceScore;

    // Build answerInfo object by copying available fields (except workNumber)
    const infoObj: Record<string, any> = {};
    const numericKeys = new Set([
      "examTotalScore",
      "practiceTotalScore",
      "examCompletedQuestions",
      "examIncorrectQuestions",
      "examTotalQuestions",
      "practiceCompletedQuestions",
      "practiceIncorrectQuestions",
      "practiceTotalQuestions",
    ]);

    Object.keys(rec).forEach((key) => {
      if (key === "workNumber") return;
      const raw = rec[key];
      if (raw === undefined || raw === "") {
        infoObj[key] = null;
        return;
      }
      if (numericKeys.has(key)) {
        const n = Number(raw);
        infoObj[key] = isNaN(n) ? null : n;
      } else {
        infoObj[key] = raw;
      }
    });

    // derive correct-answer counts from completed and incorrect counts
    const examCompleted = Number(rec.examCompletedQuestions ?? 0) || 0;
    const examIncorrect = Number(rec.examIncorrectQuestions ?? 0) || 0;
    // examCorrectQuestions = completed - incorrect (may be 0 or negative if data is inconsistent)
    infoObj.examCorrectQuestions = examCompleted - examIncorrect;

    const practiceCompleted = Number(rec.practiceCompletedQuestions ?? 0) || 0;
    const practiceIncorrect = Number(rec.practiceIncorrectQuestions ?? 0) || 0;
    // practiceCorrectQuestions = completed - incorrect
    infoObj.practiceCorrectQuestions = practiceCompleted - practiceIncorrect;

    // ensure score present
    infoObj.score = totalScore;

    return {
      workNumber: workNumber,
      answerInfo: JSON.stringify(infoObj),
    };
  });

  const payload = {
    publishAnswerInfoItemParamList: items,
  };

  isSaving.value = true;
  try {
    const res = await fetch(
      "https://bsc-gateway.sonline.hzmantu.com/bsc-store-activity/partner/publishAnswerInfo",
      {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(payload),
      },
    );

    if (!res.ok) {
      const text = await res.text().catch(() => res.statusText || "提交失败");
      showMessage("error", `保存失败: ${text}`);
      return;
    }

    const data = await res.json().catch(() => null);
    showMessage("success", "保存成功");
    successNotice.value = `已上传成功 ${lastUploadedFileName.value || fileName.value} 文件`;
    emit("uploaded", parsedData.value);
    // reset upload UI/state back to initial
    handleResetData();
  } catch (err: any) {
    showMessage("error", `保存失败: ${err?.message ?? err}`);
  } finally {
    isSaving.value = false;
  }
}
</script>

<template>
  <div class="ant-upload-body">
    <div v-if="props.disabled" class="ant-upload-mask" />
    <div
      class="ant-upload-block"
      :class="{ 'ant-drag-over': isDragging }"
      @dragover.prevent="onDragOver"
      @dragenter.prevent="onDragOver"
      @dragleave.prevent="onDragLeave"
      @drop.prevent="onDrop"
    >
      <input
        ref="fileInput"
        type="file"
        accept=".xlsx,.xls,.csv"
        style="display: none"
        @change="onFileChange"
      />
      <div>
        <button class="ant-upload-btn" type="button" @click="triggerFilePicker">
          上传文件
        </button>
        <p class="ant-upload-text">
          仅支持格式为 .xls ，.xlsx 和 .csv
          文件，最多支持导入10000个参赛人员数据
        </p>
      </div>
    </div>

    <div v-if="uploadState === 'success'" class="ant-upload-success">
      <div>
        <div class="ant-upload-success-name">
          <span class="ant-upload-success-tips">{{ fileName }}</span>
          <span class="ant-upload-btn" @click="handleResetData">删除</span>
          <span
            class="ant-upload-btn"
            :class="{ 'is-loading': isSaving }"
            @click="handleSave"
          >
            {{ isSaving ? "保存中..." : "保存" }}
          </span>
        </div>
        <div class="ant-upload-success-goods">
          成功识别{{ allGoodsNumber }}个参数者信息
        </div>
        <div class="ant-upload-error-goods">
          注：如存在重复工号或填写错误工号，会自动忽略
        </div>
      </div>
    </div>

    <div v-if="uploadState === 'error'" class="ant-upload-error">
      <div>{{ uploadErrorText }}</div>
      <div>
        <span class="ant-upload-btn" @click="handleResetData">重新上传</span>
      </div>
    </div>

    <div v-if="successNotice" class="ant-upload-notice">
      {{ successNotice }}
    </div>
  </div>
</template>
